//! Main entry point for the Rust GraphQL server
//! 
//! This binary provides a ready-to-use GraphQL server that automatically
//! generates schemas from your database structure.

use actix_cors::Cors;
use actix_web::{web, App, HttpResponse, HttpServer, Result};
use async_graphql_actix_web::{GraphQLRequest, GraphQLResponse};
use async_graphql::dynamic::Schema as DynSchema;
use tokio::sync::RwLock;

use rust_graphql::{init_tracing, Config, DatabaseIntrospector, SchemaGenerator};
use rust_graphql::schema::dynamic_builder::DynamicSchemaBuilder;
use sea_orm::Database;
use std::sync::Arc;
use tracing::{info, error};

type AppSchema = DynSchema;
type SharedSchema = std::sync::Arc<RwLock<AppSchema>>;

/// GraphQL request handler
async fn graphql(
    schema: web::Data<SharedSchema>,
    req: GraphQLRequest,
) -> GraphQLResponse {
    let current = schema.read().await;
    current.execute(req.into_inner()).await.into()
}

/// GraphQL Playground handler
async fn graphql_playground() -> Result<HttpResponse> {
    Ok(HttpResponse::Ok()
        .content_type("text/html; charset=utf-8")
        .body(async_graphql::http::playground_source(
            async_graphql::http::GraphQLPlaygroundConfig::new("/graphql"),
        )))
}

/// Health check endpoint
async fn health_check() -> Result<HttpResponse> {
    Ok(HttpResponse::Ok().json(serde_json::json!({
        "status": "healthy",
        "version": rust_graphql::VERSION
    })))
}

#[actix_web::main]
async fn main() -> anyhow::Result<()> {
    // Initialize tracing
    init_tracing();
    
    // Load configuration
    let config = Config::from_env()?;
    info!("Starting Rust GraphQL server with config: {:?}", config);
    
    // Connect to database
    let db = Database::connect(&config.database_url).await?;
    info!("Connected to database successfully");
    
    // Create introspector and schema generator
    let db_arc = Arc::new(db);
    let introspector = DatabaseIntrospector::new(db_arc.clone());
    let mut generator = SchemaGenerator::new();
    
    // Generate GraphQL schema
    info!("Generating GraphQL schema...");
    let metadata = introspector.get_database_metadata().await?;
    let schema = generator.generate_schema(&metadata).await?;
    info!("GraphQL schema generated successfully");
    
    let bind_address = format!("{}:{}", config.host, config.port);
    info!("GraphQL Playground: http://{}/graphql", bind_address);
    info!("Health check: http://{}/health", bind_address);
    
    // Build dynamic executable GraphQL schema (root fields per table)
    let dyn_builder = DynamicSchemaBuilder::new();
    let app_schema = dyn_builder.build(db_arc.clone()).await?;
    let shared_schema: SharedSchema = std::sync::Arc::new(RwLock::new(app_schema));

    // Reload handler: rebuild dynamic schema at runtime
    async fn reload(
        schema_lock: web::Data<SharedSchema>,
        db_conn: web::Data<std::sync::Arc<sea_orm::DatabaseConnection>>,
    ) -> Result<HttpResponse> {
        let dyn_builder = DynamicSchemaBuilder::new();
        match dyn_builder.build(db_conn.get_ref().clone()).await {
            Ok(new_schema) => {
                let mut guard = schema_lock.write().await;
                *guard = new_schema;
                Ok(HttpResponse::Ok().json(serde_json::json!({ "ok": true, "message": "schema reloaded" })))
            }
            Err(e) => Ok(HttpResponse::InternalServerError().json(serde_json::json!({ "ok": false, "error": e.to_string() }))),
        }
    }

    HttpServer::new(move || {
        App::new()
            // share DB for reload endpoint
            .app_data(web::Data::new(db_arc.clone()))
            // share dynamic schema with RwLock for hot reload
            .app_data(web::Data::new(shared_schema.clone()))
            .wrap(
                Cors::default()
                    .allow_any_origin()
                    .allow_any_method()
                    .allow_any_header()
                    .max_age(3600),
            )
            .route("/graphql", web::post().to(graphql))
            .route("/graphql", web::get().to(graphql_playground))
            .route("/admin/reload", web::post().to(reload))
            .route("/health", web::get().to(health_check))
    })
    .bind(&bind_address)?
    .run()
    .await
    .map_err(|e| {
        error!("Server error: {}", e);
        anyhow::anyhow!("Failed to start server: {}", e)
    })
}